package com.sam4cloud.oauth2.project.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sam4cloud.common.core.cons.HttpCode;
import com.sam4cloud.common.core.enums.DelFlagEnum;
import com.sam4cloud.common.core.enums.StatusEnum;
import com.sam4cloud.common.core.utils.AssertUtil;
import com.sam4cloud.common.core.utils.LocalDateTimeUtils;
import com.sam4cloud.common.redis.utils.RedisUtils;
import com.sam4cloud.oauth2.project.cons.AuthCodeCons;
import com.sam4cloud.oauth2.project.enums.FreezeEnum;
import com.sam4cloud.oauth2.project.mapper.OauthAccountMapper;
import com.sam4cloud.oauth2.project.model.dto.OauthAccountDTO;
import com.sam4cloud.oauth2.project.model.entity.OauthAccount;
import com.sam4cloud.oauth2.project.model.vo.AccessTokenVO;
import com.sam4cloud.oauth2.project.service.IOauthAccountService;
import com.sam4cloud.oauth2.utils.EncryptUtils;
import org.apache.commons.codec.digest.Md5Crypt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 认证账户表 服务实现类
 * </p>
 *
 * @author Sam
 * @since 2022-12-05
 */
@Service
public class OauthAccountServiceImpl extends ServiceImpl<OauthAccountMapper, OauthAccount> implements IOauthAccountService {

    @Autowired
    private RedisUtils redisUtils;

    @Override
    public void addAccount(OauthAccountDTO oauthAccountDTO) {
        OauthAccount oauthAccount = oauthAccountDTO.convert(OauthAccount.class);
        //生成策略：MC + [(随机10位数字+时间戳) 转成10进制的BigInteger后 再转成32进制的String]
        String appId = (new BigInteger(RandomUtil.randomNumbers(10) + System.currentTimeMillis(), 10).toString(32));
        oauthAccount.setAppId("MC" + appId);
        oauthAccount.setAppSecret(IdUtil.simpleUUID());
        oauthAccount.setStatus(StatusEnum.NORMAL);
        oauthAccount.setDelFlag(DelFlagEnum.NORMAL);
        boolean save = this.save(oauthAccount);
        AssertUtil.isFalse(save, "新增失败");
    }

    @Override
    public OauthAccount checkAccountValid(String appId, String appSecret, String reqIp) {
        OauthAccount oauthAccount = this.getOne(Wrappers.<OauthAccount>lambdaQuery()
                .eq(OauthAccount::getAppId, appId), false);
        AssertUtil.isNull(oauthAccount, HttpCode.INVALID_APPID_OR_SECRET, "无效的appId或者appSecret");
        String dbSecret = oauthAccount.getAppSecret();
        AssertUtil.isNotEqStr(dbSecret, appSecret, "无效的appId或者appSecret");
        FreezeEnum isFreeze = oauthAccount.getIsFreeze();
        AssertUtil.isTrue(FreezeEnum.FREEZE == isFreeze, "账户已被冻结，请联系管理员处理");
        //校验是否是白名单用户
        String creditDomain = oauthAccount.getCreditDomain();
        List<String> creditDomainList = new ArrayList<>();
        if (StrUtil.isNotEmpty(creditDomain)) {
            creditDomainList = Arrays.asList(creditDomain.split(","));
        }
        AssertUtil.isFalse(creditDomainList.contains(reqIp), "未授权的ip地址");
        return oauthAccount;
    }

    @Override
    public String createTempCode(String appId, String reqIp, String redirectUrl) {

        //校验appId的合法性
        OauthAccount oauthAccount = this.getOne(Wrappers.<OauthAccount>lambdaQuery()
                .eq(OauthAccount::getAppId, appId), false);
        AssertUtil.isNull(oauthAccount, "无效的appId");

        //此处采用nanoId进行区分 相对uuid更不容易碰撞且生成长度短于uuid
        String code = IdUtil.nanoId();
        //code值必须能区分 企业标识、应用id、请求ip
        JSONObject obj = new JSONObject();
        obj.set("ip", reqIp);
        obj.set("appId", appId);
        redisUtils.setEx(AuthCodeCons.CODE_KEY + code, JSONUtil.toJsonStr(obj), AuthCodeCons.CODE_EXPIRE, TimeUnit.MINUTES);
        return code;
    }

    @Override
    public AccessTokenVO createAccessToken(String appId, String appSecret, String reqIp) {
        //校验 appId和appSecret 的合法性
        OauthAccount oauthAccount = checkAccountValid(appId, appSecret, reqIp);
        //创建accessToken认证令牌 设置2小时有效期 刷新令牌
        long expiresIn = LocalDateTimeUtils.toEpochMilli(LocalDateTime.now().plusHours(2));
        Md5Crypt.apr1Crypt(appId, appSecret);
        //todo 生成accessToken
        String accessToken = "";
        //把accessToken更新到数据库
        boolean update = this.update(Wrappers.<OauthAccount>lambdaUpdate()
                .set(OauthAccount::getAccessToken, accessToken)
                .set(OauthAccount::getExpiresIn, expiresIn)
                .eq(OauthAccount::getId, oauthAccount.getId()));
        AssertUtil.isFalse(update, "授权失败");
        //todo 添加授权记录

        AccessTokenVO accessTokenVO = new AccessTokenVO();
        accessTokenVO.setAccessToken(accessToken);
        accessTokenVO.setExpiresIn(expiresIn);
        return accessTokenVO;
    }

    @Override
    public AccessTokenVO createTempAccessToken(String appId, String appSecret, String reqIp) {
        //校验 appId和appSecret 的合法性
        OauthAccount oauthAccount = checkAccountValid(appId, appSecret, reqIp);
        AccessTokenVO accessTokenVO = new AccessTokenVO();
        return accessTokenVO;
    }

    @Override
    public AccessTokenVO getUserInfo(String accessToken, String code) {
        AccessTokenVO accessTokenVO = new AccessTokenVO();
        return accessTokenVO;
    }

    public static void main(String[] args) {
        System.out.println(Md5Crypt.apr1Crypt("MC8uf4u6rnmvxl1y", "e9f7f91241244c369a15b48f9ab1bc44"));
        System.out.println(Md5Crypt.md5Crypt("MC8uf4u6rnmvxl1y".getBytes(StandardCharsets.UTF_8), "$1$e9f7f91241244c369a15b48f9ab1bc44"));

        String s = EncryptUtils.aesEncrypt("MC8uf4u6rnmvxl1y", "e9f7f91241244c369a15b48f9ab1bc44");
        System.out.println("s = " + s);
        String b = EncryptUtils.aesDecrypt(s, "e9f7f91241244c369a15b48f9ab1bc44");
        System.out.println("b = " + b);
    }

}
